Use @nestjs/throttler for simple per-IP rate limiting. For robust protection use rate-limiter-flexible with Redis — it tracks failures per IP and email combination, applies progressive blocking after the limit is exceeded, and is shared across all instances. Apply the guard before the Passport guard so blocked requests are rejected without hitting the database.
Key on both IP and email — prevents an attacker from rotating IPs to bypass IP-only limits.
Apply the rate limit guard before the Passport guard — rejected requests never reach bcrypt.compare().
Block for longer than the window — 5 attempts in 5 minutes should block for 15+ minutes.
Return Retry-After header so clients know when to try again — include it in the exception response.
Use Redis-backed rate limiting in production — in-memory limiters are not shared across instances.